hvm: More cleanups, particularly to %cr4 handling.
authorkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Wed, 8 Aug 2007 10:28:26 +0000 (11:28 +0100)
committerkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Wed, 8 Aug 2007 10:28:26 +0000 (11:28 +0100)
Signed-off-by: Keir Fraser <keir@xensource.com>
Signed-off-by: Xin Li <xin.b.li@intel.com>
xen/arch/x86/hvm/hvm.c
xen/arch/x86/hvm/svm/svm.c
xen/arch/x86/hvm/vioapic.c
xen/arch/x86/hvm/vmx/vmx.c
xen/arch/x86/mm/hap/hap.c
xen/arch/x86/mm/shadow/multi.c
xen/include/asm-x86/hvm/hvm.h
xen/include/asm-x86/hvm/support.h

index 2be3d8d6624bb8f91c39cbff5b0166b153954d39..c5294f6334ffa7b6815c05b3abc91c98dd847332 100644 (file)
@@ -525,16 +525,9 @@ int hvm_set_cr3(unsigned long value)
     unsigned long old_base_mfn, mfn;
     struct vcpu *v = current;
 
-    if ( paging_mode_hap(v->domain) )
-    {
-        /* HAP mode. HAP-specific code does all the hard work. */
-        v->arch.hvm_vcpu.guest_cr[3] = value;
-        paging_update_cr3(v);
-    }
-    else if ( !hvm_paging_enabled(v) )
+    if ( paging_mode_hap(v->domain) || !hvm_paging_enabled(v) )
     {
-        /* Shadow-mode, paging disabled. Just update guest CR3 value. */
-        v->arch.hvm_vcpu.guest_cr[3] = value;
+        /* Nothing to do. */
     }
     else if ( value == v->arch.hvm_vcpu.guest_cr[3] )
     {
@@ -542,7 +535,6 @@ int hvm_set_cr3(unsigned long value)
         mfn = get_mfn_from_gpfn(value >> PAGE_SHIFT);
         if ( mfn != pagetable_get_pfn(v->arch.guest_table) )
             goto bad_cr3;
-        paging_update_cr3(v);
     }
     else 
     {
@@ -558,11 +550,11 @@ int hvm_set_cr3(unsigned long value)
         if ( old_base_mfn )
             put_page(mfn_to_page(old_base_mfn));
 
-        v->arch.hvm_vcpu.guest_cr[3] = value;
         HVM_DBG_LOG(DBG_LEVEL_VMMU, "Update CR3 value = %lx", value);
-        paging_update_cr3(v);
     }
 
+    v->arch.hvm_vcpu.guest_cr[3] = value;
+    paging_update_cr3(v);
     return 1;
 
  bad_cr3:
@@ -571,6 +563,44 @@ int hvm_set_cr3(unsigned long value)
     return 0;
 }
 
+int hvm_set_cr4(unsigned long value)
+{
+    struct vcpu *v = current;
+    unsigned long old_cr;
+
+    if ( value & HVM_CR4_GUEST_RESERVED_BITS )
+    {
+        HVM_DBG_LOG(DBG_LEVEL_1,
+                    "Guest attempts to set reserved bit in CR4: %lx",
+                    value);
+        goto gpf;
+    }
+
+    if ( !(value & X86_CR4_PAE) && hvm_long_mode_enabled(v) )
+    {
+        HVM_DBG_LOG(DBG_LEVEL_1, "Guest cleared CR4.PAE while "
+                    "EFER.LMA is set");
+        goto gpf;
+    }
+
+    old_cr = v->arch.hvm_vcpu.guest_cr[4];
+    v->arch.hvm_vcpu.guest_cr[4] = value;
+    v->arch.hvm_vcpu.hw_cr[4] = value | HVM_CR4_HOST_MASK;
+    if ( paging_mode_hap(v->domain) )
+        v->arch.hvm_vcpu.hw_cr[4] &= ~X86_CR4_PAE;
+    hvm_update_guest_cr(v, 4);
+  
+    /* Modifying CR4.{PSE,PAE,PGE} invalidates all TLB entries, inc. Global. */
+    if ( (old_cr ^ value) & (X86_CR4_PSE | X86_CR4_PGE | X86_CR4_PAE) )
+        paging_update_paging_modes(v);
+
+    return 1;
+
+ gpf:
+    hvm_inject_exception(TRAP_gp_fault, 0, 0);
+    return 0;
+}
+
 /*
  * __hvm_copy():
  *  @buf  = hypervisor buffer
@@ -863,12 +893,6 @@ int hvm_do_hypercall(struct cpu_user_regs *regs)
             flush ? HVM_HCALL_invalidate : HVM_HCALL_completed);
 }
 
-void hvm_update_guest_cr3(struct vcpu *v, unsigned long guest_cr3)
-{
-    v->arch.hvm_vcpu.hw_cr[3] = guest_cr3;
-    hvm_funcs.update_guest_cr3(v);
-}
-
 static void hvm_latch_shinfo_size(struct domain *d)
 {
     /*
index a87197b33cd473b4a37b02c42a9f4ea632c013cc..b38d3d3ae72b923eb0afca7005a460c8d6653733 100644 (file)
@@ -578,10 +578,20 @@ static void svm_update_host_cr3(struct vcpu *v)
     /* SVM doesn't have a HOST_CR3 equivalent to update. */
 }
 
-static void svm_update_guest_cr3(struct vcpu *v)
+static void svm_update_guest_cr(struct vcpu *v, unsigned int cr)
 {
-    v->arch.hvm_svm.vmcb->cr3 = v->arch.hvm_vcpu.hw_cr[3];
-    svm_asid_inv_asid(v);
+    switch ( cr )
+    {
+    case 3:
+        v->arch.hvm_svm.vmcb->cr3 = v->arch.hvm_vcpu.hw_cr[3];
+        svm_asid_inv_asid(v);
+        break;
+    case 4:
+        v->arch.hvm_svm.vmcb->cr4 = v->arch.hvm_vcpu.hw_cr[4];
+        break;
+    default:
+        BUG();
+    }
 }
 
 static void svm_flush_guest_tlbs(void)
@@ -917,7 +927,7 @@ static struct hvm_function_table svm_function_table = {
     .get_segment_base     = svm_get_segment_base,
     .get_segment_register = svm_get_segment_register,
     .update_host_cr3      = svm_update_host_cr3,
-    .update_guest_cr3     = svm_update_guest_cr3,
+    .update_guest_cr      = svm_update_guest_cr,
     .flush_guest_tlbs     = svm_flush_guest_tlbs,
     .update_vtpr          = svm_update_vtpr,
     .stts                 = svm_stts,
@@ -1684,9 +1694,6 @@ static int svm_set_cr0(unsigned long value)
     return 1;
 }
 
-/*
- * Read from control registers. CR0 and CR4 are read from the shadow.
- */
 static void mov_from_cr(int cr, int gp, struct cpu_user_regs *regs)
 {
     unsigned long value = 0;
@@ -1725,13 +1732,9 @@ static void mov_from_cr(int cr, int gp, struct cpu_user_regs *regs)
     HVM_DBG_LOG(DBG_LEVEL_VMMU, "mov_from_cr: CR%d, value = %lx", cr, value);
 }
 
-
-/*
- * Write to control registers
- */
 static int mov_to_cr(int gpreg, int cr, struct cpu_user_regs *regs)
 {
-    unsigned long value, old_cr;
+    unsigned long value;
     struct vcpu *v = current;
     struct vlapic *vlapic = vcpu_vlapic(v);
     struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
@@ -1752,69 +1755,7 @@ static int mov_to_cr(int gpreg, int cr, struct cpu_user_regs *regs)
         return hvm_set_cr3(value);
 
     case 4:
-        if ( value & HVM_CR4_GUEST_RESERVED_BITS )
-        {
-            HVM_DBG_LOG(DBG_LEVEL_1,
-                        "Guest attempts to set reserved bit in CR4: %lx",
-                        value);
-            svm_inject_exception(v, TRAP_gp_fault, 1, 0);
-            break;
-        }
-
-        if ( paging_mode_hap(v->domain) )
-        {
-            v->arch.hvm_vcpu.guest_cr[4] = value;
-            vmcb->cr4 = value | (HVM_CR4_HOST_MASK & ~X86_CR4_PAE);
-            paging_update_paging_modes(v);
-            break;
-        }
-
-        old_cr = v->arch.hvm_vcpu.guest_cr[4];
-        if ( value & X86_CR4_PAE && !(old_cr & X86_CR4_PAE) )
-        {
-            if ( hvm_paging_enabled(v) )
-            {
-#if CONFIG_PAGING_LEVELS >= 3
-                /* The guest is a 32-bit PAE guest. */
-                unsigned long mfn, old_base_mfn;
-                mfn = get_mfn_from_gpfn(v->arch.hvm_vcpu.guest_cr[3] >> PAGE_SHIFT);
-                if ( !mfn_valid(mfn) || 
-                     !get_page(mfn_to_page(mfn), v->domain) )
-                    goto bad_cr3;
-
-                /*
-                 * Now arch.guest_table points to machine physical.
-                 */
-                old_base_mfn = pagetable_get_pfn(v->arch.guest_table);
-                v->arch.guest_table = pagetable_from_pfn(mfn);
-                if ( old_base_mfn )
-                    put_page(mfn_to_page(old_base_mfn));
-                paging_update_paging_modes(v);
-
-                HVM_DBG_LOG(DBG_LEVEL_VMMU, 
-                            "Update CR3 value = %lx, mfn = %lx",
-                            v->arch.hvm_vcpu.guest_cr[3], mfn);
-#endif
-            }
-        } 
-        else if ( !(value & X86_CR4_PAE) )
-        {
-            if ( hvm_long_mode_enabled(v) )
-            {
-                svm_inject_exception(v, TRAP_gp_fault, 1, 0);
-            }
-        }
-
-        v->arch.hvm_vcpu.guest_cr[4] = value;
-        vmcb->cr4 = value | HVM_CR4_HOST_MASK;
-  
-        /*
-         * Writing to CR4 to modify the PSE, PGE, or PAE flag invalidates
-         * all TLB entries except global entries.
-         */
-        if ((old_cr ^ value) & (X86_CR4_PSE | X86_CR4_PGE | X86_CR4_PAE))
-            paging_update_paging_modes(v);
-        break;
+        return hvm_set_cr4(value);
 
     case 8:
         vlapic_set_reg(vlapic, APIC_TASKPRI, ((value & 0x0F) << 4));
@@ -1828,19 +1769,11 @@ static int mov_to_cr(int gpreg, int cr, struct cpu_user_regs *regs)
     }
 
     return 1;
-
- bad_cr3:
-    gdprintk(XENLOG_ERR, "Invalid CR3\n");
-    domain_crash(v->domain);
-    return 0;
 }
 
-
-#define ARR_SIZE(x) (sizeof(x) / sizeof(x[0]))
-
-
-static int svm_cr_access(struct vcpu *v, unsigned int cr, unsigned int type,
-                         struct cpu_user_regs *regs)
+static void svm_cr_access(
+    struct vcpu *v, unsigned int cr, unsigned int type,
+    struct cpu_user_regs *regs)
 {
     struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
     int inst_len = 0;
@@ -1865,12 +1798,12 @@ static int svm_cr_access(struct vcpu *v, unsigned int cr, unsigned int type,
     if ( type == TYPE_MOV_TO_CR )
     {
         inst_len = __get_instruction_length_from_list(
-            v, list_a, ARR_SIZE(list_a), &buffer[index], &match);
+            v, list_a, ARRAY_SIZE(list_a), &buffer[index], &match);
     }
     else /* type == TYPE_MOV_FROM_CR */
     {
         inst_len = __get_instruction_length_from_list(
-            v, list_b, ARR_SIZE(list_b), &buffer[index], &match);
+            v, list_b, ARRAY_SIZE(list_b), &buffer[index], &match);
     }
 
     ASSERT(inst_len > 0);
@@ -1883,7 +1816,8 @@ static int svm_cr_access(struct vcpu *v, unsigned int cr, unsigned int type,
 
     HVM_DBG_LOG(DBG_LEVEL_1, "eip = %lx", (unsigned long) vmcb->rip);
 
-    switch (match) 
+    switch ( match )
+
     {
     case INSTR_MOV2CR:
         gpreg = decode_src_reg(prefix, buffer[index+2]);
@@ -1974,9 +1908,8 @@ static int svm_cr_access(struct vcpu *v, unsigned int cr, unsigned int type,
 
     ASSERT(inst_len);
 
-    __update_guest_eip(vmcb, inst_len);
-    
-    return result;
+    if ( result )
+        __update_guest_eip(vmcb, inst_len);
 }
 
 static void svm_do_msr_access(
index 016d91c3e45f47007b74da44cc5dc01eba07e833..e73ad688814d4d680dc6cfa1e61c2648da31ab2c 100644 (file)
 /* HACK: Route IRQ0 only to VCPU0 to prevent time jumps. */
 #define IRQ0_SPECIAL_ROUTING 1
 
-#if defined(__ia64__)
-#define opt_hvm_debug_level opt_vmx_debug_level
-#endif
-
 static void vioapic_deliver(struct hvm_hw_vioapic *vioapic, int irq);
 
 static unsigned long vioapic_read_indirect(struct hvm_hw_vioapic *vioapic,
index f8fd25a3ae5821071b06f7947ea652c164022865..84adda7ac1d800b6823e7b707b11fccc84abf2c7 100644 (file)
@@ -1087,11 +1087,25 @@ static void vmx_update_host_cr3(struct vcpu *v)
     vmx_vmcs_exit(v);
 }
 
-static void vmx_update_guest_cr3(struct vcpu *v)
+static void vmx_update_guest_cr(struct vcpu *v, unsigned int cr)
 {
     ASSERT((v == current) || !vcpu_runnable(v));
+
     vmx_vmcs_enter(v);
-    __vmwrite(GUEST_CR3, v->arch.hvm_vcpu.hw_cr[3]);
+
+    switch ( cr )
+    {
+    case 3:
+        __vmwrite(GUEST_CR3, v->arch.hvm_vcpu.hw_cr[3]);
+        break;
+    case 4:
+        __vmwrite(GUEST_CR4, v->arch.hvm_vcpu.hw_cr[4]);
+        __vmwrite(CR4_READ_SHADOW, v->arch.hvm_vcpu.guest_cr[4]);
+        break;
+    default:
+        BUG();
+    }
+
     vmx_vmcs_exit(v);
 }
 
@@ -1157,7 +1171,7 @@ static struct hvm_function_table vmx_function_table = {
     .get_segment_base     = vmx_get_segment_base,
     .get_segment_register = vmx_get_segment_register,
     .update_host_cr3      = vmx_update_host_cr3,
-    .update_guest_cr3     = vmx_update_guest_cr3,
+    .update_guest_cr      = vmx_update_guest_cr,
     .flush_guest_tlbs     = vmx_flush_guest_tlbs,
     .update_vtpr          = vmx_update_vtpr,
     .stts                 = vmx_stts,
@@ -2263,12 +2277,9 @@ static int vmx_set_cr0(unsigned long value)
     CASE_ ## T ## ET_REG(R15, r15)
 #endif
 
-/*
- * Write to control registers
- */
 static int mov_to_cr(int gp, int cr, struct cpu_user_regs *regs)
 {
-    unsigned long value, old_cr;
+    unsigned long value;
     struct vcpu *v = current;
     struct vlapic *vlapic = vcpu_vlapic(v);
 
@@ -2303,66 +2314,7 @@ static int mov_to_cr(int gp, int cr, struct cpu_user_regs *regs)
         return hvm_set_cr3(value);
 
     case 4:
-        old_cr = v->arch.hvm_vcpu.guest_cr[4];
-
-        if ( value & HVM_CR4_GUEST_RESERVED_BITS )
-        {
-            HVM_DBG_LOG(DBG_LEVEL_1,
-                        "Guest attempts to set reserved bit in CR4: %lx",
-                        value);
-            vmx_inject_hw_exception(v, TRAP_gp_fault, 0);
-            return 0;
-        }
-
-        if ( (value & X86_CR4_PAE) && !(old_cr & X86_CR4_PAE) )
-        {
-            if ( hvm_paging_enabled(v) )
-            {
-#if CONFIG_PAGING_LEVELS >= 3
-                /* The guest is a 32-bit PAE guest. */
-                unsigned long mfn, old_base_mfn;
-                mfn = get_mfn_from_gpfn(v->arch.hvm_vcpu.guest_cr[3] >> PAGE_SHIFT);
-                if ( !mfn_valid(mfn) ||
-                     !get_page(mfn_to_page(mfn), v->domain) )
-                    goto bad_cr3;
-
-                /*
-                 * Now arch.guest_table points to machine physical.
-                 */
-                old_base_mfn = pagetable_get_pfn(v->arch.guest_table);
-                v->arch.guest_table = pagetable_from_pfn(mfn);
-                if ( old_base_mfn )
-                    put_page(mfn_to_page(old_base_mfn));
-
-                HVM_DBG_LOG(DBG_LEVEL_VMMU,
-                            "Update CR3 value = %lx, mfn = %lx",
-                            v->arch.hvm_vcpu.guest_cr[3], mfn);
-#endif
-            }
-        }
-        else if ( !(value & X86_CR4_PAE) )
-        {
-            if ( unlikely(hvm_long_mode_enabled(v)) )
-            {
-                HVM_DBG_LOG(DBG_LEVEL_1, "Guest cleared CR4.PAE while "
-                            "EFER.LMA is set");
-                vmx_inject_hw_exception(v, TRAP_gp_fault, 0);
-                return 0;
-            }
-        }
-
-        __vmwrite(GUEST_CR4, value | HVM_CR4_HOST_MASK);
-        v->arch.hvm_vcpu.guest_cr[4] = value;
-        __vmwrite(CR4_READ_SHADOW, v->arch.hvm_vcpu.guest_cr[4]);
-
-        /*
-         * Writing to CR4 to modify the PSE, PGE, or PAE flag invalidates
-         * all TLB entries except global entries.
-         */
-        if ( (old_cr ^ value) & (X86_CR4_PSE | X86_CR4_PGE | X86_CR4_PAE) )
-            paging_update_paging_modes(v);
-
-        break;
+        return hvm_set_cr4(value);
 
     case 8:
         vlapic_set_reg(vlapic, APIC_TASKPRI, ((value & 0x0F) << 4));
@@ -2370,14 +2322,11 @@ static int mov_to_cr(int gp, int cr, struct cpu_user_regs *regs)
 
     default:
         gdprintk(XENLOG_ERR, "invalid cr: %d\n", cr);
-        domain_crash(v->domain);
-        return 0;
+        goto exit_and_crash;
     }
 
     return 1;
 
- bad_cr3:
-    gdprintk(XENLOG_ERR, "Invalid CR3\n");
  exit_and_crash:
     domain_crash(v->domain);
     return 0;
@@ -2438,7 +2387,8 @@ static int vmx_cr_access(unsigned long exit_qualification,
     unsigned long value;
     struct vcpu *v = current;
 
-    switch ( exit_qualification & CONTROL_REG_ACCESS_TYPE ) {
+    switch ( exit_qualification & CONTROL_REG_ACCESS_TYPE )
+    {
     case TYPE_MOV_TO_CR:
         gp = exit_qualification & CONTROL_REG_ACCESS_REG;
         cr = exit_qualification & CONTROL_REG_ACCESS_NUM;
index df61d581626ac2226f3497118222769786eee915..63f16756598f9dd90b91ff9a359ba8f88174a221 100644 (file)
@@ -605,7 +605,8 @@ static int hap_invlpg(struct vcpu *v, unsigned long va)
 
 static void hap_update_cr3(struct vcpu *v, int do_locking)
 {
-    hvm_update_guest_cr3(v, v->arch.hvm_vcpu.guest_cr[3]);
+    v->arch.hvm_vcpu.hw_cr[3] = v->arch.hvm_vcpu.guest_cr[3];
+    hvm_update_guest_cr(v, 3);
 }
 
 static void hap_update_paging_modes(struct vcpu *v)
@@ -631,7 +632,7 @@ static void hap_update_paging_modes(struct vcpu *v)
     }
 
     /* CR3 is effectively updated by a mode change. Flush ASIDs, etc. */
-    hvm_update_guest_cr3(v, v->arch.hvm_vcpu.guest_cr[3]);
+    hap_update_cr3(v, 0);
 
     hap_unlock(d);
 }
index 49b84954b41d0df02a9f45866202f1f7f2c3919d..84857e43e12303f49951966eda77610a505c9773 100644 (file)
@@ -3483,7 +3483,7 @@ sh_update_cr3(struct vcpu *v, int do_locking)
  * Paravirtual guests should set v->arch.guest_table (and guest_table_user,
  * if appropriate).
  * HVM guests should also make sure hvm_get_guest_cntl_reg(v, 3) works;
- * this function will call hvm_update_guest_cr3() to tell them where the 
+ * this function will call hvm_update_guest_cr(v, 3) to tell them where the 
  * shadow tables are.
  * If do_locking != 0, assume we are being called from outside the 
  * shadow code, and must take and release the shadow lock; otherwise 
@@ -3725,11 +3725,14 @@ sh_update_cr3(struct vcpu *v, int do_locking)
         ASSERT(is_hvm_domain(d));
 #if SHADOW_PAGING_LEVELS == 3
         /* 2-on-3 or 3-on-3: Use the PAE shadow l3 table we just fabricated */
-        hvm_update_guest_cr3(v, virt_to_maddr(&v->arch.paging.shadow.l3table));
+        v->arch.hvm_vcpu.hw_cr[3] =
+            virt_to_maddr(&v->arch.paging.shadow.l3table);
 #else
         /* 2-on-2 or 4-on-4: Just use the shadow top-level directly */
-        hvm_update_guest_cr3(v, pagetable_get_paddr(v->arch.shadow_table[0]));
+        v->arch.hvm_vcpu.hw_cr[3] =
+            pagetable_get_paddr(v->arch.shadow_table[0]);
 #endif
+        hvm_update_guest_cr(v, 3);
     }
 
     /* Fix up the linear pagetable mappings */
index 223299ff2d7787a5a640e6cff7d72b304e913c3f..91c550585eca83ff71b4391df616344c00533aa8 100644 (file)
@@ -107,14 +107,14 @@ struct hvm_function_table {
                                  struct segment_register *reg);
 
     /* 
-     * Re-set the value of CR3 that Xen runs on when handling VM exits
+     * Re-set the value of CR3 that Xen runs on when handling VM exits.
      */
     void (*update_host_cr3)(struct vcpu *v);
 
     /*
-     * Called to inform HVM layer that a guest cr3 has changed
+     * Called to inform HVM layer that a guest control register has changed.
      */
-    void (*update_guest_cr3)(struct vcpu *v);
+    void (*update_guest_cr)(struct vcpu *v, unsigned int cr);
 
     /*
      * Called to ensure than all guest-specific mappings in a tagged TLB
@@ -220,7 +220,10 @@ hvm_update_vtpr(struct vcpu *v, unsigned long value)
     hvm_funcs.update_vtpr(v, value);
 }
 
-void hvm_update_guest_cr3(struct vcpu *v, unsigned long guest_cr3);
+static inline void hvm_update_guest_cr(struct vcpu *v, unsigned int cr)
+{
+    hvm_funcs.update_guest_cr(v, cr);
+}
 
 static inline void 
 hvm_flush_guest_tlbs(void)
index d98ffa4a177bfc02d37057f294803422c782d541..17f72b676167c8ffdf8420d3c86f94252d9a3a41 100644 (file)
@@ -235,5 +235,6 @@ void hvm_hlt(unsigned long rflags);
 void hvm_triple_fault(void);
 
 int hvm_set_cr3(unsigned long value);
+int hvm_set_cr4(unsigned long value);
 
 #endif /* __ASM_X86_HVM_SUPPORT_H__ */